﻿// The Nova Project by Ken Beckett.
// Copyright (C) 2007-2012 Inevitable Software, all rights reserved.
// Released under the Common Development and Distribution License, CDDL-1.0: http://opensource.org/licenses/cddl1.php

using Nova.Parsing;
using Nova.Rendering;

namespace Nova.CodeDOM
{
    /// <summary>
    /// Represents a user-defined conversion operator.
    /// </summary>
    /// <remarks>
    /// Conversion operators must have either the implicit or explicit modifier, a single parameter,
    /// and either the parameter type OR the destination type (return type) must be the containing type.
    /// They can only be defined by class or struct types, and must be public and static.
    /// </remarks>
    public class ConversionOperatorDecl : OperatorDecl
    {
        #region /* CONSTRUCTORS */

        /// <summary>
        /// Create a <see cref="ConversionOperatorDecl"/>.
        /// </summary>
        public ConversionOperatorDecl(Expression destinationType, Modifiers modifiers, CodeObject body, ParameterDecl parameter)
            : base(GetInternalName(modifiers), destinationType, modifiers, body, new[] { parameter })
        { }

        /// <summary>
        /// Create a <see cref="ConversionOperatorDecl"/>.
        /// </summary>
        public ConversionOperatorDecl(Expression destinationType, Modifiers modifiers, ParameterDecl parameter)
            : base(GetInternalName(modifiers), destinationType, modifiers, new[] { parameter })
        { }

        #endregion

        #region /* PROPERTIES */

        /// <summary>
        /// True if the conversion is explicit.
        /// </summary>
        public bool IsExplicit
        {
            get { return _modifiers.HasFlag(Modifiers.Explicit); }
        }

        /// <summary>
        /// True if the conversion is implicit.
        /// </summary>
        public bool IsImplicit
        {
            get { return _modifiers.HasFlag(Modifiers.Implicit); }
        }

        #endregion

        #region /* METHODS */

        private static string GetInternalName(Modifiers modifiers)
        {
            string name = Operator.NamePrefix;
            if (modifiers.HasFlag(Modifiers.Implicit))
                name += Modifiers.Implicit.ToString();
            else if (modifiers.HasFlag(Modifiers.Explicit))
                name += Modifiers.Explicit.ToString();
            return name;
        }

        /// <summary>
        /// Get the full name of the <see cref="INamedCodeObject"/>, including any namespace name.
        /// </summary>
        /// <param name="descriptive">True to display type parameters and method parameters, otherwise false.</param>
        public override string GetFullName(bool descriptive)
        {
            string name = (IsExplicit ? "explicit" : (IsImplicit ? "implicit" : "")) + " " + ParseToken + " " + _returnType.GetDescription();
            if (descriptive)
                name += GetParametersAsString();
            if (_parent is TypeDecl)
                name = ((TypeDecl)_parent).GetFullName(descriptive) + "." + name;
            return name;
        }

        #endregion

        #region /* PARSING */

        /// <summary>
        /// Parse a <see cref="ConversionOperatorDecl"/>.
        /// </summary>
        public ConversionOperatorDecl(Parser parser, CodeObject parent, ParseFlags flags)
            : base(parser, parent, false, flags)
        {
            parser.NextToken();                                 // Move past 'operator'
            _modifiers = ModifiersHelpers.Parse(parser, this);  // Parse any modifiers in reverse from the Unused list
            _name = GetInternalName(_modifiers);                // Get the name
            ParseUnusedAnnotations(parser, this, false);        // Parse attributes and/or doc comments from the Unused list
            SetField(ref _returnType, Expression.Parse(parser, this, true, Expression.ParseTokenStartGroup), false);
            ParseParameters(parser);
            ParseTerminatorOrBody(parser, flags);
        }

        #endregion

        #region /* RENDERING */

        internal override void AsTextName(CodeWriter writer, RenderFlags flags)
        {
            RenderFlags passFlags = (flags & RenderFlags.PassMask);
            _returnType.AsText(writer, passFlags);
        }

        protected override void AsTextStatement(CodeWriter writer, RenderFlags flags)
        {
            UpdateLineCol(writer, flags);
            writer.Write(ParseToken + " ");
            if (flags.HasFlag(RenderFlags.Description) && _parent is TypeDecl)
            {
                ((TypeDecl)_parent).AsTextName(writer, flags);
                writer.Write(Dot.ParseToken);
            }
            AsTextName(writer, flags);
        }

        #endregion
    }
}
